package org.xiaoyu.rpc;

import cn.hutool.core.util.ClassUtil;
import com.xiaoyu.context.support.ClassPathXmlApplicationContext;
import com.xiaoyu.stereotype.Component;
import lombok.Data;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import static org.xiaoyu.rpc.config.PropertiesConfig.properties;

/**
 * Created By Have
 * 2021/7/13 15:46
 */
public class RpcClientContainer {
    private static ClassPathXmlApplicationContext applicationContext;
    private static final String RESOURCES_PROPERTIES = "/application.properties";

    public void init(String... basePackages) {
        try {
            properties.load(this.getClass().getResourceAsStream(RESOURCES_PROPERTIES));
        } catch (IOException e) {
            e.printStackTrace();
        }
        String[] xmls = new String[2];
        xmls[0] = "classpath:rpc.xml";
        xmls[1] = "classpath:spring.xml";
        applicationContext = new ClassPathXmlApplicationContext(xmls);

        new Thread(new Runnable() {
            @Override
            public void run() {
                Map<Class<?>, List<Method>> classListMap = scanService(basePackages);
                Set<Map.Entry<Class<?>, List<Method>>> entries = classListMap.entrySet();
                int i = 0;
                Map<String, Pair> commandMap = new HashMap<>();
                StringBuilder stringBuilder = new StringBuilder();
                for (Map.Entry<Class<?>, List<Method>> entry : entries) {
                    List<Method> methods = entry.getValue();
                    Class<?> clazz = entry.getKey();
                    stringBuilder.append("Service ");
                    stringBuilder.append(clazz.getSimpleName());
                    stringBuilder.append(":\n");
                    for (; i < methods.size(); i++) {
                        stringBuilder.append("\t ");
                        stringBuilder.append(i + 1);
                        stringBuilder.append(":");
                        stringBuilder.append(methods.get(i).getName());
                        stringBuilder.append("\n");
                        Pair pair = new Pair(methods.get(i), clazz);
                        commandMap.put(String.valueOf(i+1), pair);
                    }
                }
                while (true) {
                    System.out.println("****************************************************************************************");
                    System.out.println(stringBuilder);
                    System.out.println("请输入以上所支持的服务编号调用:");
                    Scanner scanner = new Scanner(System.in);
                    String stringCommand = scanner.nextLine();
                    Pair pair = commandMap.get(stringCommand);
                    System.out.println("****************************************************************************************");
                    if (pair == null) {
                        System.out.println("请输入正确编号");
                        continue;
                    }
                    Class<?> clazz = pair.getClazz();
                    Method method = pair.getMethod();
                    Object bean = applicationContext.getBean(clazz);
                    try {
                        method.invoke(bean, null);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();
    }


    private  Map<Class<?>, List<Method>> scanService(String... basePackages) {
        // 扫描路径下所有带有 RpcReference 的服务
        Map<Class<?>, List<Method>> serviceMap = new HashMap<>();
        for (String basePackage : basePackages) {
            Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);
            for (Class<?> clazz : classes) {
                List<Method> list = new ArrayList<>();
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    IsRpcService annotation = method.getAnnotation(IsRpcService.class);
                    if (annotation == null) continue;
                    list.add(method);
                }
                if (list.size() > 0) {
                    serviceMap.put(clazz, list);
                }
            }
        }
        return serviceMap;
    }

    @Data
    static class Pair {
        Method method;
        Class<?> clazz;

        public Pair(Method method, Class<?> clazz) {
            this.method = method;
            this.clazz = clazz;
        }
    }
}
